package com.iteaj.iot.client.protocol;

import com.iteaj.iot.ProtocolException;
import com.iteaj.iot.client.ClientMessage;
import com.iteaj.iot.client.ClientProtocolException;
import com.iteaj.iot.client.SocketClient;
import com.iteaj.iot.consts.ExecStatus;

import java.nio.channels.ClosedChannelException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 同步的客户端主动协议
 * @param <C>
 */
public abstract class ClientInitiativeSyncProtocol<C extends ClientMessage> extends ClientInitiativeProtocol<C>{

    public ClientInitiativeSyncProtocol() {
        this.sync(3000); // 默认同步时间
    }

    /**
     * 平台主动向外发起请求
     */
    @Override
    protected void sendRequest() throws ProtocolException {
        try {
            /**
             * 获取对应的客户端 {@link #getClientKey()}
             */
            SocketClient client = getIotClient();

            // 构建请求报文
            buildRequestMessage();

            // 检查是否会出现死锁
            syncDeadValidate(client);

            // 请求报文处理
            requestMessageHandle(client);

            synchronized (client) {
                // 发起请求, 写出请求报文
                this.writeAndFlush(client);

                // 校验写报文状态
                if(getExecStatus() != ExecStatus.success) {
                    this.exec(getProtocolHandle()); return;
                }

                /**
                 * 是同步请求
                 */
                if(isSyncRequest()) {
                    // 如果发送成功等待报文响应
                    boolean await = getDownLatch().await(getTimeout(), TimeUnit.MILLISECONDS);
                    if(!await) { // 响应超时
                        this.execTimeoutHandle();
                    }

                    // 同步执行业务
                    this.exec(getProtocolHandle());
                } else if(!isRelation()) { // 既不是同步也不是异步, 直接执行业务
                    this.exec(getProtocolHandle());
                }
            }

        } catch (InterruptedException e) {
            throw new ClientProtocolException(e);
        }
    }

    @Override
    protected void requestMessageHandle(SocketClient client) {
        super.requestMessageHandle(client);

        if(this.requestMessage().getHead().getMessageId() == null) {
            this.requestMessage().getHead()
                    .setMessageId(requestMessage().getChannelId());
        }
    }

    @Override
    public ClientInitiativeSyncProtocol setExecStatus(ExecStatus execStatus) {
        return (ClientInitiativeSyncProtocol) super.setExecStatus(execStatus);
    }
}
